/*
 * This file is part of JBotSim.
 * 
 *    JBotSim is free software: you can redistribute it and/or modify it
 *    under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *  
 *    Authors:
 *    Arnaud Casteigts		<casteig@site.uottawa.ca>
 */
package jbotsim;

import _examples.global;
import java.util.HashMap;
import java.util.Vector;
import static jbotsim.Message.messageDelay;

import jbotsim.event.ClockListener;
import jbotsim.event.MessagebrListener;

public class Messagebr {

    /**
     * The source of this message.
     */
    public final Node source;
    /**
     * The destination of this message.
     */
    public final Node destination;
    /**
     * The content of this message, which may be an object of any kind. The
     * object is not copied while sending the message, so the reference is
     * shared between the source and the destination(s).
     */
    public final Object content;
    protected final boolean retryMode;
    protected static boolean debuggingMode = false;
    protected static int nbDeliveredMessagebrs = 0;
    protected static int messageDelay = 1;

    Messagebr(Node source, Node destination, Object content) {
        this(source, destination, content, false);
    }

    Messagebr(Node source, Node destination, Object content, boolean retryMode) {
        assert (destination != null || !retryMode);
        this.source = source;
        this.destination = destination;
        this.content = content;
        this.retryMode = retryMode;
    }

    public String toString() {
        String dest = (destination == null) ? "all" + source.getNeighbors() : destination.toString();
        return source + " -> " + dest + ": " + content;
    }

    public String toStringPR() {
        //String dest=(destination==null)?"all"+source.getNeighbors():destination.toString();
        return "" + content;
    }

    static class MessagebrEngine implements ClockListener {

        private HashMap<Messagebr, Integer> currentMessagebrs = new HashMap<Messagebr, Integer>();
        private Topology topo;

        MessagebrEngine(Topology topo) {
            this.topo = topo;
            Clock.addClockListener(this, 1);
        }

        public void onClock() {

            for (Node n : topo.nodes) {
                Vector<Messagebr> toRemove = new Vector<Messagebr>();
                for (Messagebr m : n.sendQueuebr) {
                    if (m.destination != null && m.source.getOutLinkTo(m.destination) == null) {
                        if (!m.retryMode) {
                            toRemove.add(m);
                        }
                    } else {
                        currentMessagebrs.put(m, messageDelay);
                        toRemove.add(m);
                    }
                }
                n.sendQueuebr.removeAll(toRemove);
            }
            for (Messagebr m : new HashMap<Messagebr, Integer>(currentMessagebrs).keySet()) {
                int remainingDelay = currentMessagebrs.get(m);
                if (remainingDelay == 1) {
                    currentMessagebrs.remove(m);
                    if (m.destination != null) {
                        if (m.source.getOutLinkTo(m.destination) != null) {
                            deliverMessagebrTo(m, m.destination);
                        } else {
                            if (m.retryMode) {
                                m.source.sendQueuebr.add(m);
                            }
                        }
                    } else {
                        for (Link l : m.source.getOutLinks()) {
                            deliverMessagebrTo(m, l.destination);
                        }
                    }
                } else {
                    if (m.source.getOutLinkTo(m.destination) != null) {
                        currentMessagebrs.put(m, remainingDelay - 1);
                    } else {
                        currentMessagebrs.remove(m);
                        if (m.retryMode) {
                            m.source.sendQueuebr.add(m);
                        }
                    }
                }
            }
        }

        protected void deliverMessagebrTo(Messagebr m, Node dest) {
            dest.mailBoxbr.add(m);
            if (Messagebr.debuggingMode) {
                System.err.println((Clock.currentTime() - messageDelay) + "->" + Clock.currentTime() + ": "
                        + m.source + "->" + dest + ": " + m.content + " ("
                        + m.content.getClass().getSimpleName() + ")");
            }
            nbDeliveredMessagebrs++;
            if (dest.shouldrecv() != 0) {
                //���ܻ���bug��node�����˴���������ж�
                for (MessagebrListener ml : dest.messagebrListeners) {
                    ml.onMessagebr(m);
                }
            }

        }
    }

    /**
     * Returns the number of clock steps separating the effective delivery of a
     * message from its sending through the <tt>Node.send</tt> method.
     */
    public static int getMessagebrDelay() {
        return Messagebr.messageDelay;
    }

    /**
     * Sets the number of clock steps separating the effective delivery of a
     * message from its sending through the <tt>Node.send</tt> method. The
     * minimum is 1 (delivery occurring at the next clock step).
     *
     * @param delay The message delay.
     */
    public static void setMessagebrDelay(int delay) {
        Messagebr.messageDelay = Math.max(delay, 1);
    }

    /**
     * Causes all messages to be printed on <tt>System.err</tt> at delivery
     * time.
     *
     * @param debugMode <tt>true</tt> to enable, <tt>false</tt> to disable.
     */
    public static void setDebuggingMode(boolean debugMode) {
        Messagebr.debuggingMode = debugMode;
    }

    /**
     * Returns the number of messages successfully delivered so far. Useful for
     * statistics..
     */
    public static int nbDeliveredMessagebrs() {
        return nbDeliveredMessagebrs;
    }
}
